/**
 * @file
 *           FSOperations.h 
 * @brief
 *          
 *
 * @par Author (last changes):
 *          - Alexandru Chica
 *          
 * @par Description:
 *	Class that deals with file system operations (both logical and phisycal)
 *
 * @par Module-History:
 * @verbatim
 *  Date        Author				Reason
 *  16.01.2010  Alexandru Chica     Creation
 *  20.01.2010  Alexandru Chica     Made class singleton
 *
 * @endverbatim
 **/

#pragma once

#include <windows.h>
#include <map>
#include <string>
#include <vector>

typedef enum
{
  IS_FILE, IS_FOLDER
} ftype_e;

typedef struct FileData
{
  FileData() :
    fileType(IS_FILE), dwSize(0), dwTimeCreated(0), dwTimeModified(0),
        dwTimeAccessed(0), bWriteLock(false), version(0)
  {
  }

  ftype_e fileType;
  DWORD dwSize;
  DWORD dwTimeCreated;
  DWORD dwTimeModified;
  DWORD dwTimeAccessed;
  bool bWriteLock;
  int version;
} FileData_t;

typedef std::pair<char*, ftype_e> LSElement_t;
typedef std::vector<LSElement_t> LSResult_t;

class FSOperations
{
public:

  /**
   * Returns an instance of this class (singleton)
   * @return class instance
   */
  static FSOperations*
  getInstance();

  /**
   * Initializes the logical FS (usually done in the constructor, but can be done manually with another path)
   * @param dirPath Root of the FS
   * @return void
   */
  void
  initializeLogicalFile(char* dirPath);

  /**
   * Cleans up resources for the object (serializes memory map and deletes internal map and contents)
   * @return void
   */
  void
  cleanUp();

  /**
   * Writes memory file content to file. Usually called in initialize and cleanUp. Can be called manually for on demand serialization
   */
  void
  serializeMemoryMap();

  /**
   * Creates a file. Timestamps will be created automatically. Write lock will be set on unlocked.
   * @param szPath Path of the file
   * @param type Type of file: file or folder
   * @param size Size of the file in bytes, default is 0 on creation
   * @return True for success, false for error
   */
  bool
  createFile(char* szPath, ftype_e type, DWORD size = 0);

  /**
   * Deletes a file (both phisically and logically)
   * @param szPath Path of the file
   * @param bRecursive True if deletion should be recursive (delete all files and subfolders)
   * @return True for success, false for error
   */
  bool
  deleteFile(char* szPath, bool bRecursive = false);

  /**
   * Modifies an existing path (moves a file/folder and all its content)
   * @param szPath The old path
   * @param szNewPath The new path
   * @return True for success, false for error
   */
  bool
  modifyPath(char* szPath, char* szNewPath);

  /**
   * Copies a file/folder and all its content
   * @param szPath The old path
   * @param szNewPath The new path (no trailing backslash!)
   * @param bOverwrite true if existent file will be overwritten, false otherwise
   * @return True for success, false for error
   */
  bool
  copyFile(char* szPath, char* szNewPath, bool bOverwrite = false);

  /**
   * Modifies size of a file
   * @param szPath Path of the file
   * @param size New size of the file
   * @return True for success, false for error
   */
  bool
  modifySize(char* szPath, DWORD size);

  /**
   * Updates timestamp for creation time
   * @param szPath Path of the file
   */
  void
  updateTimeCreated(char* szPath);

  /**
   * Updates timestamp for modification time
   * @param szPath Path of the file
   */
  void
  updateTimeModified(char* szPath);

  /**
   * Updates timestamp for access time
   * @param szPath Path of the file
   */
  void
  updateTimeAccessed(char* szPath);

  /**
   * Updates the write lock guard
   * @param szPath Path of the file
   * @param bWriteLock value with which to update the lock
   * @return True for success, false for error (may yield error if 1.file does not exist or 2. file is already locked)
   */
  bool
  setWriteLock(char *szPath, bool bWriteLock);

  /**
   * Returns the write lock guard
   * @param szPath Path of the file
   * @param bLocked true if file is locked, false if unlocked
   * @return true for success, false otherwise
   */
  bool
  getWriteLock(char *szPath, bool &bLocked);

  /**
   * Returns information on a file
   * @param szPath path to the file
   * @param outFileData file information for the specified path
   * @return True for success, false for error (file does not exist)
   */
  bool
  queryFileData(char* szPath, FileData_t& outFileData);

  /**
   * Opens a file - updates time accessed
   * @param szPath Path to the file
   * @param bWrite true if file is opened for writing, false if file is opened for reading
   * @return True for success, false for error
   */
  bool
  openFile(char* szPath, bool bWrite);

  /**
   * Closes a file - updates time accessed
   * @param szPath Path to the file
   * @return true for success, false for error
   */
  bool
  closeFile(char* szPath);

  /**
   * Reads data from a specified file
   * @param szPath Path to the file
   * @param buf Data returned by the call (has to be allocated by caller)
   * @param dataRead Size of data to read (bytes)
   * @return data actually read (bytes)
   */
  int
  readFromFile(char* szPath, char* buf, int dataToRead);

	/**
   * Reads data from a specified file
   * @param szPath Path to the file
   * @param buf Data returned by the call (has to be allocated by caller)
   * @param offset Position from which the data will be read
   * @param dataRead Size of data to read (bytes)
   * @return data actually read (bytes)
   */
  int readFromFile(char* szPath, char* buf, long offset, int dataToRead);

  /**
   * Writes data to file
   * @param szPath path to the file
   * @param buf Data to be written
   * @param dataToWrite size of data to be written (bytes)
   * @return size of data written (bytes)
   */
  int
  writeToFile(char* szPath, char* buf, int dataToWrite);

	/**
   * Writes data to file
   * @param szPath path to the file
   * @param buf Data to be written
   * @param offset Position at which to write
   * @param dataToWrite size of data to be written (bytes)
   * @return size of data written (bytes)
   */
	int FSOperations::writeToFile(char* szPath, char* buf, long offset, int dataToWrite);

  /**
   * Lists the content of a directory. Fails if szPath represents a file or if szPath is invalid
   * @param szPath Path of the dir to list
   * @param outDirList directory listing (vector of pairs <path, file type>) PATH MUST BE DEALLOCATED BY CALLER
   * @param bRecursive flag if the search is recursive or not (default is off)
   * @return true for success, false for error
   */
  bool
  listDirectory(char* szPath, LSResult_t& outDirList, bool bRecursive = false);

  /**
   * Checks if a file/folder exists on disk
   * @param szPath Path to the file
   * @return true - file exists, false - file does not exist
   */
  bool
  checkPathExists(char* szPath);

  /**
   * Retrieves ENTIRE content of a file
   * @param szPath Path to the file
   * @param outFileData FileData_t structure associated to the file
   * @param outFileContent Content of the file (is allocated inside this method, will be deallocated by the caller)
   * @return true for success, false for error
   */
  bool
      retrieveFileData(char* szPath, FileData_t& outFileData,
          void*& outFileContent);

  /**
   * Increments file version
   * @param szPath Path to the file
   * @return true for success, false for error (path does not exist);
   */
  bool
  incrementVersion(char* szPath);

  /**
   * Decrements file version
   * @param szPath Path to the file
   * @return true for success, false for error (path does not exist);
   */
  bool
  decrementVersion(char* szPath);

  /**
   * Returns file version
   * @param szPath Path to the file
   * @param outVersion file version for the specified file
   * @return true for success, false for error (path does not exist);
   */
  bool
  checkVersion(char* szPath, int& outVersion);

  /**
   * Returns total space (bytes)
   */
  DWORD
  getTotalSpace();

  /**
   * Returns free space (bytes)
   */
  DWORD
  getFreeSpace();

private:

  FSOperations(void);
  ~FSOperations(void);

  static FSOperations* m_pInstance;

  //map comparer, so that we can search the keys more easily
  struct StrComparer
  {
    bool
    operator()(const char* s1, const char* s2) const
    {
      return strcmp(s1, s2) < 0;
    }
  };

  typedef std::map<char*, FileData_t, StrComparer> FileDataContainer_t;

  //map that contains all files and folders
  FileDataContainer_t m_FSContent;

  //path prefix
  char m_pathPrefix[MAX_PATH];

  //file object for the logical fs
  FILE* m_pFile;

  //how much occupied space there is in the FS
  DWORD m_occupiedFSSize;

  //helper functions

  //scans a directory recursively and adds data to the file list
  void
  scanDirectory(char* dirPath);

  //builds an absolute path
  void
  buildAbsolutePath(const char* pathPrefix, const char* pathSuffix,
      std::string& absolutePath);

  //inserts data into memory map
  void
  insertIntoMemoryMap(char* szPath, FileData_t& fileData);

  //updates memory map data
  void
  updateMemoryMap(char* szOldPath, char* szNewPath);

  //checks that the current directory or file is not a subdirectory of the previous (for non-recursive listing)
  bool
  checkDirOnCurrentLevel(const char* prevDir, const char* currentDir);

  //inserts a copy into memory map (updates only timestamps
  void
  insertCopyIntoMemoryMap(char* szPath, FileData_t& fileData);

  //used in delete, to sort the deletion vector
  //bigger (deeper) paths first -> first paths to delete (leafs)
  static bool
  sortPredicate(char* path1, char* path2)
  {
    return strlen(path1) > strlen(path2);
  }

  //given a directory structure, this method adds each directory to the memory map, in case it isn't there already
  void checkAddDirectoriesToMemoryMap(std::string fullDirPath);

  //returns relative path starting from an absolute path
  void getRelativePath(char* szAbsolutePath, char*& szOutRelativePath);

};
